package cloud.hedou.abp.auth

import cloud.hedou.abp.identity.AbpUser
import cloud.hedou.abp.identity.RemoteIdentityService
import org.springframework.core.MethodParameter
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.method.support.ModelAndViewContainer

/** 从请求中解析用户信息的解析器，可解析类型为[AbpUser]及名称为`userId`和`tenantId`的String类型的参数。*/
class AbpAuthArgumentResolver(
    private val remoteIdentityService: RemoteIdentityService
) : HandlerMethodArgumentResolver {

    override fun supportsParameter(parameter: MethodParameter): Boolean {
        val parameterName = parameter.parameterName
        val parameterType = parameter.parameterType
        val hasAnnotation = parameter.parameterAnnotations.any {
            it.annotationClass.qualifiedName == "springfox.documentation.annotations.ApiIgnore"
        }
        return hasAnnotation && (parameterType == AbpUser::class.java || parameterType == String::class.java && parameterName in ANALYSABLE_PARAMETER_NAMES)
    }

    override fun resolveArgument(
        parameter: MethodParameter,
        mavContainer: ModelAndViewContainer?,
        webRequest: NativeWebRequest,
        binderFactory: WebDataBinderFactory?
    ): Any? {
        // 设置当前请求
        RequestContextHolder.setRequestAttributes(webRequest, true)
        // 解析参数
        return when {
            USER_ID == parameter.parameterName -> CurrentUser.id
            TENANT_ID == parameter.parameterName -> CurrentUser.tenantId
            AbpUser::class.java == parameter.parameterType -> CurrentUser.id.let(remoteIdentityService::getUserById)
            else -> null
        }
    }

    private companion object {

        private const val USER_ID = "userId"
        private const val TENANT_ID = "tenantId"
        private val ANALYSABLE_PARAMETER_NAMES = arrayOf(USER_ID, TENANT_ID)

    }

}